package com.trailwalker.traceability.activity;

import com.trailwalker.JavaUtils.file.FileUtils;
import com.trailwalker.JavaUtils.log.LogUtils;
import com.trailwalker.JavaUtils.string.StringUtils;
import com.trailwalker.javafx.utils.storage.PreferencesManager;
import com.trailwalker.javafx.utils.style.StyleUtils;
import com.trailwalker.javafx.utils.thread.ThreadUtils;
import com.trailwalker.traceability.entitiy.LogInfo;
import com.trailwalker.traceability.entitiy.body.ProcessRequestBody;
import com.trailwalker.traceability.entitiy.body.ProcessResponseBody;
import com.trailwalker.traceability.entitiy.enum_entity.LogInfoType;
import com.trailwalker.traceability.entitiy.enum_entity.ProcessType;
import com.trailwalker.traceability.net.result.ApiResult;
import com.trailwalker.traceability.net.result.ResponseBodyCode;
import com.trailwalker.traceability.service.ApiDataService;
import com.trailwalker.traceability.service.DirectoryListenService;
import io.reactivex.functions.Consumer;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.DirectoryChooser;
import javafx.util.Callback;

import java.io.File;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.util.function.BiConsumer;

/**
 * @author Trailwalker
 * @create 2021-11-18 11:35
 */
public abstract class BaseTestActvity extends BaseActivity {

    private static final String TAG = BaseTestActvity.class.getSimpleName();

    private AnchorPane mApContent;
    private ProcessType mProcessType;
    private Button mBtnChoose;
    private ObservableList<LogInfo> mLogList;
    private String mListenedDirectoryPath;
    /**
     * 单板测试监听路径
     */
    //private String mListenedSingleBoardTestDirectoryPath;
    /**
     * 成品测试监听路径
     */
    //private String mListenedFinishedProductTestDirectoryPath;
    /**
     * 终检测试监听路径
     */
    //private String mListenedFinalInspectionDirectoryPath;

    private PreferencesManager mPreferencesManager;
    private ListView<LogInfo> mLvLog;
    private DirectoryListenService mSingleBoardTestDirectoryListenService;
    private DirectoryListenService mFinishedProductTestDirectoryListenService;
    private DirectoryListenService mFinalInspectionDirectoryListenService;

    @Override
    protected void afterSuperCreate() {
        initLoad();
        initView();
        initData();
        initListener();
        onReady();
    }

    private void onReady() {
        startListen();
    }

    private void startListen() {
        mSingleBoardTestDirectoryListenService = startListen(mSingleBoardTestDirectoryListenService, ProcessType.SINGLE_BOARD_TEST);
        mFinishedProductTestDirectoryListenService = startListen(mFinishedProductTestDirectoryListenService, ProcessType.FINISHED_PRODUCT_TEST);
        mFinalInspectionDirectoryListenService = startListen(mFinalInspectionDirectoryListenService, ProcessType.FINAL_INSPECTION);
    }

    private DirectoryListenService startListen(DirectoryListenService sourceDirectoryListenService, ProcessType processType) {
        if (sourceDirectoryListenService != null) {
            sourceDirectoryListenService.finish();
        }
        String listenedDirectoryPath = getListenedDirectoryPath(processType);
        DirectoryListenService directoryListenService = null;
        if (isListenedDirectoryPathExist(listenedDirectoryPath)) {
            directoryListenService = new DirectoryListenService(listenedDirectoryPath, new BiConsumer<String, WatchEvent.Kind<?>>() {
                @Override
                public void accept(String filePath, WatchEvent.Kind<?> kind) {
                    switch (kind.toString()) {
                        case "ENTRY_CREATE":
                            printVerboseLog("文件 " + filePath + " 被创建了");
                            break;
                        case "ENTRY_DELETE":
                            printVerboseLog("文件 " + filePath + " 被删除了");
                            break;
                        case "ENTRY_MODIFY":
                            printVerboseLog("文件 " + filePath + " 被修改了");
                            break;
                        default:
                            break;
                    }
                    if (StringUtils.isValid(filePath)) {
                        try {
                            File file = FileUtils.getFile(filePath);
                            if (FileUtils.isFile(file)) {
                                traceUpload(file, processType);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            LogUtils.logE(e);
                        }
                    }
                }
            });
            directoryListenService.start();
        }
        return directoryListenService;
    }

    private void initListener() {
        if (mBtnChoose != null) {
            mBtnChoose.setOnMouseClicked(new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent event) {
                    if (event.getButton().equals(MouseButton.PRIMARY)) {
                        DirectoryChooser directoryChooser = new DirectoryChooser();
                        if (isListenedDirectoryPathExist(mListenedDirectoryPath)) {
                            try {
                                File file = FileUtils.getFile(mListenedDirectoryPath);
                                directoryChooser.setInitialDirectory(file);
                            } catch (Exception e) {
                                e.printStackTrace();
                                LogUtils.logE(e);
                            }
                        }
                        directoryChooser.setTitle("请选择");
                        File file = directoryChooser.showDialog(mApplication.getStage());
                        if (file != null) {
                            mListenedDirectoryPath = file.getAbsolutePath();
                            mPreferencesManager.putString(mProcessType.getName() + "listenedDirectoryPath", mListenedDirectoryPath);
                            initData();
                            startListen();
                        }
                    }
                }
            });
        }
    }

    private boolean isListenedDirectoryPathExist(String listenedDirectoryPath) {
        if (StringUtils.isValid(listenedDirectoryPath)) {
            File file = FileUtils.getFile(listenedDirectoryPath);
            if (file.isDirectory() && file.exists()) {
                return true;
            }
        }
        return false;
    }

    private void traceUpload(File file, ProcessType processType) {
        printVerboseLog("正在上传文件: " + file.getAbsolutePath());
        ProcessRequestBody processRequestBody = new ProcessRequestBody();
        processRequestBody.setProcessName(processType.getName());
        processRequestBody.setFile(file);
        ApiDataService.getInstance().traceUpload(mApplication, processRequestBody, new Consumer<ApiResult<ProcessResponseBody>>() {
            @Override
            public void accept(ApiResult<ProcessResponseBody> processResponseBodyApiResult) throws Exception {
                int result = processResponseBodyApiResult.getResult();
                if (result == ResponseBodyCode.SUCCESS) {
                    printInfoLog(result + " : " + processResponseBodyApiResult.getMsg());
                } else {
                    printErrorLog(result + " : " + processResponseBodyApiResult.getMsg());
                }
            }
        });
    }

    private void printVerboseLog(String log) {
        LogInfo logInfo = new LogInfo();
        logInfo.setLogInfoType(LogInfoType.VERBOSE);
        logInfo.setLog(log);
        printLog(logInfo);
    }

    private void printInfoLog(String log) {
        LogInfo logInfo = new LogInfo();
        logInfo.setLogInfoType(LogInfoType.INFO);
        logInfo.setLog(log);
        printLog(logInfo);
    }

    private void printWarnLog(String log) {
        LogInfo logInfo = new LogInfo();
        logInfo.setLogInfoType(LogInfoType.WARN);
        logInfo.setLog(log);
        printLog(logInfo);
    }

    private void printErrorLog(String log) {
        LogInfo logInfo = new LogInfo();
        logInfo.setLogInfoType(LogInfoType.ERROR);
        logInfo.setLog(log);
        printLog(logInfo);
    }

    private void printLog(LogInfo logInfo) {
        ThreadUtils.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (mLogList.size() >= 100) {
                    mLogList.remove(0);
                }
                mLogList.add(logInfo);
                LogUtils.log(TAG, logInfo.getLog());
                mLvLog.scrollTo(mLogList.size() - 1);
            }
        });
    }

    private void initData() {
        mLogList.clear();
        printVerboseLog("Microsoft Windows [版本 10.0.22000.318]");
        printVerboseLog("(c) Microsoft Corporation。保留所有权利。");
        printVerboseLog("");
        if (mBtnChoose != null) {
            String btnChooseText;
            if (!isListenedDirectoryPathExist(mListenedDirectoryPath)) {
                btnChooseText = "选择待监听文件夹";
                printWarnLog("警告: 未指定待监听文件夹");
            } else {
                btnChooseText = "重新选择";

                boolean isListenedDirectoryPathExist = false;

                String mListenedSingleBoardTestDirectoryPath = getListenedDirectoryPath(ProcessType.SINGLE_BOARD_TEST);
                if (isListenedDirectoryPathExist(mListenedSingleBoardTestDirectoryPath)) {
                    isListenedDirectoryPathExist = true;
                    printInfoLog("正在监听: " + mListenedSingleBoardTestDirectoryPath + " ...");
                }

                String mListenedFinishedProductTestDirectoryPath = getListenedDirectoryPath(ProcessType.FINISHED_PRODUCT_TEST);
                if (isListenedDirectoryPathExist(mListenedFinishedProductTestDirectoryPath)) {
                    isListenedDirectoryPathExist = true;
                    printInfoLog("正在监听: " + mListenedFinishedProductTestDirectoryPath + " ...");
                }

                String mListenedFinalInspectionDirectoryPath = getListenedDirectoryPath(ProcessType.FINAL_INSPECTION);
                if (isListenedDirectoryPathExist(mListenedFinalInspectionDirectoryPath)) {
                    isListenedDirectoryPathExist = true;
                    printInfoLog("正在监听: " + mListenedFinalInspectionDirectoryPath + " ...");
                }

                if (!isListenedDirectoryPathExist) {
                    printErrorLog(mListenedDirectoryPath + " 下未找到可监听文件夹");
                }
            }
            mBtnChoose.setText(btnChooseText);
        }
    }

    private void initView() {
        BorderPane bpContent = new BorderPane();
        mApContent.getChildren().add(bpContent);
        mAdaptUtils.setMatchParentAnchorPane(bpContent);

        HBox hbTitle = new HBox();
        bpContent.setTop(hbTitle);
        hbTitle.setAlignment(Pos.CENTER);
        mAdaptUtils.setScaleHeight(hbTitle, 150);

        Label lblTitle = new Label();
        hbTitle.getChildren().add(lblTitle);
        lblTitle.setText(mProcessType.getName());
        mAdaptUtils.setScaleTextSize(lblTitle, 40);
        StyleUtils.setTextColor(lblTitle, "#000000");

        initCenterView(bpContent);
    }

    private void initCenterView(BorderPane bpContent) {
        BorderPane bpCenter = new BorderPane();
        bpContent.setCenter(bpCenter);

        VBox vbLeft = new VBox();
        bpCenter.setLeft(vbLeft);
        vbLeft.setAlignment(Pos.CENTER);
        mAdaptUtils.setScaleWidth(vbLeft, 500);

        mBtnChoose = new Button();
        vbLeft.getChildren().add(mBtnChoose);
        StyleUtils.setTextColor(mBtnChoose, "#ffffff");
        mAdaptUtils.setScaleTextSize(mBtnChoose, 30);
        mAdaptUtils.setScaleSize(mBtnChoose, 300, 60);
        mAdaptUtils.setBackgroundRadius(mBtnChoose, 10);
        mBtnChoose.getStyleClass().add("positive_button");
        mBtnChoose.setCursor(Cursor.HAND);

        BorderPane bpLogContainer = new BorderPane();
        bpCenter.setCenter(bpLogContainer);
        StyleUtils.setBackgroundColor(bpLogContainer, "#000000");

        HBox hbLogTitleContainer = new HBox();
        bpLogContainer.setTop(hbLogTitleContainer);
        hbLogTitleContainer.setAlignment(Pos.CENTER_LEFT);
        mAdaptUtils.setScalePadding(hbLogTitleContainer, 15);

        Label lblLogTitle = new Label();
        hbLogTitleContainer.getChildren().add(lblLogTitle);
        mAdaptUtils.setScaleTextSize(lblLogTitle, 25);
        StyleUtils.setTextColor(lblLogTitle, "#ffffff");
        lblLogTitle.setText("日志");

        mLvLog = new ListView<>(mLogList);
        bpLogContainer.setCenter(mLvLog);
        mLvLog.setId("lvLog");
        mLvLog.setPadding(javafx.geometry.Insets.EMPTY);
        mLvLog.setCellFactory(new Callback<ListView<LogInfo>, ListCell<LogInfo>>() {
            @Override
            public ListCell<LogInfo> call(ListView<LogInfo> param) {

                ListCell<LogInfo> listCell = new ListCell<LogInfo>() {
                    @Override
                    protected void updateItem(LogInfo item, boolean empty) {
                        super.updateItem(item, empty);

                        HBox hbItemContainer = new HBox();
                        mAdaptUtils.setScalePaddingLeft(hbItemContainer, 10);
                        mAdaptUtils.setScalePaddingTopAndBottom(hbItemContainer, 3);
                        hbItemContainer.setAlignment(Pos.CENTER_LEFT);

                        if (!empty) {
                            Label lblLog = new Label();
                            hbItemContainer.getChildren().add(lblLog);
                            switch (item.getLogInfoType()) {
                                case VERBOSE:
                                    StyleUtils.setTextColor(lblLog, "#ffffff");
                                    break;
                                case INFO:
                                    StyleUtils.setTextColor(lblLog, "#00ff00");
                                    break;
                                case WARN:
                                    StyleUtils.setTextColor(lblLog, "#ffff00");
                                    break;
                                case ERROR:
                                    StyleUtils.setTextColor(lblLog, "#ff0000");
                                    break;
                                default:
                                    StyleUtils.setTextColor(lblLog, "#ffffff");
                                    break;
                            }
                            mAdaptUtils.setScaleTextSize(lblLog, 20);
                            lblLog.setText(item.getLog());
                        }

                        this.setGraphic(hbItemContainer);
                    }
                };
                listCell.setPadding(Insets.EMPTY);
                return listCell;
            }
        });
    }

    private void initLoad() {
        mProcessType = getProcessType();
        mLogList = FXCollections.observableArrayList();
        mPreferencesManager = PreferencesManager.getInstance();
        mListenedDirectoryPath = mPreferencesManager.getString(mProcessType.getName() + "listenedDirectoryPath", "");
    }

    @Override
    protected Node initContentView() {
        mApContent = new AnchorPane();
        return mApContent;
    }

    protected abstract ProcessType getProcessType();

    @Override
    public void onDestroy() {
        LogUtils.log(TAG, "onDestroy");
        super.onDestroy();
        if (mSingleBoardTestDirectoryListenService != null) {
            mSingleBoardTestDirectoryListenService.finish();
        }
        if (mFinishedProductTestDirectoryListenService != null) {
            mFinishedProductTestDirectoryListenService.finish();
        }
        if (mFinalInspectionDirectoryListenService != null) {
            mFinalInspectionDirectoryListenService.finish();
        }
    }

    public String getListenedDirectoryPath(ProcessType processType) {
        String listenedDirectoryPath = "";
        switch (processType) {
            case SINGLE_BOARD_TEST:
                listenedDirectoryPath = Paths.get(mListenedDirectoryPath, "单板").toFile().getAbsolutePath();
                break;
            case FINISHED_PRODUCT_TEST:
                listenedDirectoryPath = Paths.get(mListenedDirectoryPath, "成品").toFile().getAbsolutePath();
                break;
            case FINAL_INSPECTION:
                listenedDirectoryPath = Paths.get(mListenedDirectoryPath, "终检").toFile().getAbsolutePath();
                break;
            default:
                break;
        }
        return listenedDirectoryPath;
    }
}
